import os
import random
import matplotlib.pyplot as plt
import numpy as np
from pathlib import Path
import json
from cbs import CBSSolver

def get_data_folder():
    """获取数据文件夹路径"""
    current_dir = os.path.dirname(os.path.abspath(__file__))
    data_folder = os.path.join(current_dir, "data")
    os.makedirs(data_folder, exist_ok=True)
    return data_folder

class MatrixModule:
    @staticmethod
    def generate_matrix(rows, cols, num_obstacles):
        """生成环境矩阵"""
        max_obstacles = int(rows * cols * 0.3)  # 设置最大障碍物数量为30%
        num_obstacles = min(num_obstacles, max_obstacles)  # 限制障碍物数量
        
        attempts = 0
        max_attempts = 50
        
        while attempts < max_attempts:
            # 初始化空矩阵
            matrix = [['.' for _ in range(cols)] for _ in range(rows)]
            
            # 随机放置障碍物
            obstacles = 0
            obstacle_positions = set()
            
            while obstacles < num_obstacles:
                x = random.randint(0, rows-1)
                y = random.randint(0, cols-1)
                
                # 避免在边缘位置放置过多障碍物
                if (x, y) not in obstacle_positions and random.random() < 0.7:
                    matrix[x][y] = '@'
                    obstacle_positions.add((x, y))
                    obstacles += 1
            
            # 检查矩阵的连通性
            # 找到第一个非障碍物位置作为起点
            start = None
            for i in range(rows):
                for j in range(cols):
                    if matrix[i][j] == '.':
                        start = (i, j)
                        break
                if start:
                    break
            
            if not start:
                attempts += 1
                continue
                
            # 检查是否所有非障碍物点都可达
            reachable = set()
            queue = [start]
            reachable.add(start)
            
            while queue:
                x, y = queue.pop(0)
                for dx, dy in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
                    new_x, new_y = x + dx, y + dy
                    if (0 <= new_x < rows and 0 <= new_y < cols and 
                        matrix[new_x][new_y] == '.' and 
                        (new_x, new_y) not in reachable):
                        queue.append((new_x, new_y))
                        reachable.add((new_x, new_y))
            
            # 检查是否所有非障碍物点都可达
            all_free_points = set((i, j) for i in range(rows) for j in range(cols) 
                                if matrix[i][j] == '.')
            
            if len(reachable) == len(all_free_points):
                # 保存矩阵，确保每个字符之间都用空格分隔
                output_file = os.path.join(get_data_folder(), "generated_matrix.txt")
                with open(output_file, 'w', encoding='utf-8') as f:
                    f.write(f"{rows} {cols}\n")
                    for row in matrix:
                        formatted_line = ' '.join(str(cell) for cell in row)
                        f.write(formatted_line + '\n')
                return matrix
            
            attempts += 1
        
        raise ValueError("无法生成有效的连通矩阵，请减少障碍物数量或增加矩阵大小")

class PointsModule:
    @staticmethod
    def generate_point_pairs(num_pairs):
        """生成点对"""
        matrix_file = os.path.join(get_data_folder(), "generated_matrix.txt")
        
        try:
            with open(matrix_file, 'r') as f:
                # 读取矩阵尺寸
                rows, cols = map(int, f.readline().split())
                # 读取矩阵，注意每个字符间有空格
                matrix = []
                for _ in range(rows):
                    row = f.readline().strip().split()
                    matrix.append(row)

            # 获取所有可用点（非障碍物位置）
            available_points = []
            for i in range(rows):
                for j in range(cols):
                    if matrix[i][j] == '.':
                        available_points.append((i, j))

            # 检查可用点数量是否足够
            if len(available_points) < 2:
                raise ValueError("没有足够的可用点来生成点对")

            # 计算可以生成的最大点对数
            max_pairs = len(available_points) // 2
            if num_pairs > max_pairs:
                raise ValueError(f"请求的点对数量({num_pairs})超过了可用空间允许的最大值({max_pairs})")

            # 生成点对
            points = []
            available_points_set = set(available_points)  # 转换为集合以提高查找效率
            
            for _ in range(num_pairs):
                # 从剩余可用点中随机选择起点
                start = random.choice(list(available_points_set))
                available_points_set.remove(start)
                
                # 从剩余可用点中随机选择终点
                end = random.choice(list(available_points_set))
                available_points_set.remove(end)
                
                # 添加点对
                points.append((start[0], start[1], end[0], end[1]))

            # 保存点对到文件
            output_file = os.path.join(get_data_folder(), "point_pairs.txt")
            with open(output_file, 'w') as f:
                f.write(f"{len(points)}\n")
                for point in points:
                    f.write(f"{point[0]} {point[1]} {point[2]} {point[3]}\n")
            
            return points
            
        except Exception as e:
            raise Exception(f"生成点对失败: {str(e)}")

class CombinerModule:
    @staticmethod
    def combine_files(num_layers=4):
        """组合矩阵和点对文件"""
        current_dir = os.path.dirname(os.path.abspath(__file__))
        data_dir = os.path.join(current_dir, "data")
        
        matrix_file = os.path.join(data_dir, "generated_matrix.txt")
        points_file = os.path.join(data_dir, "point_pairs.txt")
        output_file = os.path.join(data_dir, "combined_output.txt")

        try:
            # 读取矩阵文件的所有内容
            with open(matrix_file, 'r', encoding='utf-8') as f:
                matrix_lines = f.readlines()
                
            with open(output_file, 'w', encoding='utf-8') as out_f:
                # 写入层数
                out_f.write(f"{num_layers}\n")
                
                # 直接复制原始矩阵内容 num_layers 次
                for _ in range(num_layers):
                    out_f.writelines(matrix_lines)
                
                # 写入点对数据
                with open(points_file, 'r', encoding='utf-8') as f:
                    points_content = f.read()
                    out_f.write(points_content)
                    
        except Exception as e:
            raise Exception(f"组合文件失败: {str(e)}")

import os
import math
import matplotlib
matplotlib.use('Agg')  # 使用非交互式后端
import matplotlib.pyplot as plt

class QuadrantModule:
    @staticmethod
    def process_and_save_quadrants():
        """处理并保存象限数据"""
        try:
            # 读取数据文件
            current_dir = os.path.dirname(os.path.abspath(__file__))
            data_dir = os.path.join(current_dir, "data")
            matrix_file = os.path.join(data_dir, "generated_matrix.txt")
            points_file = os.path.join(data_dir, "point_pairs.txt")
            
            # 读取矩阵数据
            with open(matrix_file, 'r', encoding='utf-8') as f:
                # 读取矩阵尺寸
                rows, cols = map(int, f.readline().strip().split())
                # 读取矩阵内容
                matrix = []
                for _ in range(rows):
                    line = f.readline().strip().split()
                    row = [1 if x == '@' else 0 for x in line]
                    matrix.append(row)
            
            # 读取点对数据
            with open(points_file, 'r', encoding='utf-8') as f:
                # 读取点对数量
                num_pairs = int(f.readline().strip())
                # 读取所有点对
                segments = []
                for _ in range(num_pairs):
                    line = f.readline().strip()
                    if line:
                        x1, y1, x2, y2 = map(int, line.split())
                        segments.append((x1, y1, x2, y2))

            # 按象限分类线段
            quadrants = []
            for i in range(4):
                quadrants.append({'matrix': matrix, 'segments': []})
            
            # 根据方向角对线段进行分类
            for seg in segments:
                x1, y1, x2, y2 = seg
                dx, dy = x2 - x1, y2 - y1
                if dx == 0 and dy == 0:
                    continue
                
                angle = math.degrees(math.atan2(dy, dx))
                if angle < 0:
                    angle += 360
                    
                quadrant_idx = int(angle // 90)
                quadrants[quadrant_idx]['segments'].append(seg)

            # 保存每个象限的数据
            for i, quadrant in enumerate(quadrants):
                filename = os.path.join(data_dir, f"quadrant_{i+1}.txt")
                with open(filename, 'w', encoding='utf-8') as f:
                    # 写入矩阵尺寸
                    f.write(f"{rows} {cols}\n")
                    # 写入矩阵
                    for row in quadrant['matrix']:
                        f.write(' '.join(['@' if cell else '.' for cell in row]) + '\n')
                    # 写入该象限的点对数量和数据
                    f.write(f"{len(quadrant['segments'])}\n")
                    for seg in quadrant['segments']:
                        f.write(' '.join(map(str, seg)) + '\n')

            # 生成可视化
            QuadrantModule.generate_visualization(quadrants)
            
            return quadrants
            
        except Exception as e:
            raise Exception(f"象限处理失败: {str(e)}")

    @staticmethod
    def generate_visualization(quadrants):
        """生成象限可视化图表"""
        plt.figure(figsize=(12, 12))
        colors = ['#FF6B6B', '#4ECDC4', '#556270', '#C44D58']
        quadrant_names = ['I', 'II', 'III', 'IV']

        all_x = []
        all_y = []
        for quadrant in quadrants:
            for seg in quadrant['segments']:
                x1, y1, x2, y2 = seg
                all_x.extend([x1, x2])
                all_y.extend([y1, y2])

        if not all_x or not all_y:
            raise Exception("没有找到有效的路径点")

        x_range = max(all_x) - min(all_x)
        y_range = max(all_y) - min(all_y)
        max_range = max(x_range, y_range) or 10
        padding = max_range * 0.15

        for i, quadrant in enumerate(quadrants):
            plt.subplot(2, 2, i + 1)
            plt.title(f'Quadrant {quadrant_names[i]} (n={len(quadrant["segments"])})')
            plt.gca().set_facecolor('#F7F7F7')
            
            plt.xlim(min(all_x) - padding, max(all_x) + padding)
            plt.ylim(min(all_y) - padding, max(all_y) + padding)
            plt.grid(True, linestyle='--', alpha=0.3)
            plt.axhline(y=0, color='black', linewidth=0.5)
            plt.axvline(x=0, color='black', linewidth=0.5)

            for seg in quadrant['segments']:
                x1, y1, x2, y2 = seg
                plt.arrow(x1, y1, x2-x1, y2-y1,
                         color=colors[i],
                         head_width=max_range*0.02,
                         head_length=max_range*0.03,
                         length_includes_head=True,
                         linewidth=1.5,
                         alpha=0.8)

            plt.axis('equal')

        plt.tight_layout(pad=3.0)
        save_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 
                                "data", "quadrant_visualization.png")
        plt.savefig(save_path, dpi=150, bbox_inches='tight', facecolor='white')
        plt.close()

class PlanningModule:
    @staticmethod
    def plan_paths(quadrant_file):
        """执行单个象限的路径规划"""
        try:
            # 读取地图和点对数据
            with open(quadrant_file, 'r', encoding='utf-8') as f:
                lines = f.readlines()
                current_line = 0
                
                # 读取矩阵大小和地图
                rows, cols = map(int, lines[current_line].strip().split())
                current_line += 1
                
                my_map = []
                for _ in range(rows):
                    line = lines[current_line].strip().split()
                    row = [(c == '@') for c in line]
                    my_map.append(row)
                    current_line += 1
                
                # 读取点对数量
                num_pairs = int(lines[current_line].strip())
                current_line += 1
                
                # 读取所有点对
                starts = []
                goals = []
                for _ in range(num_pairs):
                    if current_line >= len(lines):
                        break
                    line = lines[current_line].strip()
                    if line:
                        x1, y1, x2, y2 = map(int, line.split())
                        starts.append((x1, y1))
                        goals.append((x2, y2))
                    current_line += 1

                if not starts or not goals:
                    return {"paths": []}, 0, 0

                # 尝试路径规划
                try:
                    solver = CBSSolver(my_map, starts, goals)
                    paths = solver.find_solution()
                    
                    if paths:  # 如果成功找到路径
                        result = {
                            'map': my_map,
                            'starts': starts,
                            'goals': goals,
                            'paths': paths
                        }
                        return result, len(starts), sum(len(p) - 1 for p in paths)
                except BaseException as e:
                    print(f"路径规划失败: {str(e)}")
                    return {"paths": []}, len(starts), 0

                return {"paths": []}, len(starts), 0
                
        except Exception as e:
            print(f"读取或处理数据时出错: {str(e)}")
            return {"paths": []}, 0, 0

    @staticmethod
    def run_planning():
        """执行所有象限的路径规划"""
        try:
            current_dir = os.path.dirname(os.path.abspath(__file__))
            data_dir = os.path.join(current_dir, 'data')
            results = []
            total_paths = 0
            successful_paths = 0
            total_cost = 0

            for quadrant in range(1, 5):
                quadrant_file = os.path.join(data_dir, f'quadrant_{quadrant}.txt')
                if not os.path.exists(quadrant_file):
                    continue

                print(f"\n处理象限 {quadrant}...")
                result, requested_paths, path_cost = PlanningModule.plan_paths(quadrant_file)
                
                if result and result.get('paths'):
                    total_cost += path_cost
                    successful_paths += len(result['paths'])
                    result['quadrant'] = quadrant
                    results.append(result)
                    
                total_paths += requested_paths

            # 检查是否有成功的规划结果
            if not results:
                raise Exception("所有象限的路径规划都失败了")

            # 保存结果
            output_file = os.path.join(data_dir, 'planning_results.json')
            with open(output_file, 'w') as f:
                json.dump(results, f, indent=4)

            return True, successful_paths, total_paths, total_cost

        except Exception as e:
            print(f"执行路径规划失败: {str(e)}")
            return False, 0, 0, 0
